package bmaso.purest.mock;

import java.io.IOException;
import java.io.StringReader;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import static junit.framework.Assert.*;

/**
 * Collection of helper methods for performing Xquery-based assertions on
 * XML data.
 */
public class AssertXPath {
	public static void assertXPathEquals(String xmlContent, String msg,
			String expectedValue, String xpath) throws IOException, SAXException,
			XPathExpressionException {
		Document doc = domFromSerializedXml(xmlContent);
		String result = evaluateWithStringResult(doc, xpath);
		assertEquals(msg, expectedValue, result);
	}
	
	public static void assertXPathEmptyOrMissing(String xmlContent, String msg,
			String xpath) throws IOException, SAXException, XPathExpressionException {
		Document doc = domFromSerializedXml(xmlContent);
		NodeList result = (NodeList)evaluateWithNodeResult(doc, xpath);
		
		//...for each result node, if it is an Element then then element
		//   must have no children...
		for(int ii=0; ii<result.getLength(); ii++) {
			Node node = result.item(ii);

			assertFalse(msg, node.hasChildNodes());
		}
	}

	private static Document domFromSerializedXml(String xmlContent)
			throws IOException, SAXException {
		try {
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			DocumentBuilder builder = factory.newDocumentBuilder();
			return builder.parse(new InputSource(new StringReader(xmlContent)));
		} catch (ParserConfigurationException pce) {
			//...should never happen...
			throw new Error(pce);
		}
	}
	
	private static String evaluateWithStringResult(Document dom, String xpath)
			throws XPathExpressionException {
		return (String)newXPath().evaluate(xpath, dom, XPathConstants.STRING);
	}
	
	private static NodeList evaluateWithNodeResult(Document dom, String xpath)
			throws XPathExpressionException {
		return (NodeList)newXPath().evaluate(xpath, dom, XPathConstants.NODESET);
	}
	
	private static XPath newXPath() {
		XPathFactory factory = XPathFactory.newInstance();
		return factory.newXPath();
	}
}
